package org.xx.armory.commons;

import java.util.stream.IntStream;

import static java.lang.System.arraycopy;
import static java.util.Arrays.copyOf;
import static java.util.Arrays.spliterator;
import static java.util.stream.StreamSupport.intStream;
import static org.xx.armory.commons.Validators.rejectIfNull;
import static org.xx.armory.commons.Validators.rejectIfOutOfRange;

/**
 * 包含 {@code int} 类型的列表。
 */
public class IntArrayList {
    private int[] array;
    private int size;

    /**
     * 初始化列表。
     *
     * @param initializations
     *         用于初始化列表的初始值。
     */
    public IntArrayList(
            int... initializations
    ) {
        if (initializations == null) {
            this.array = new int[16];
            this.size = 0;
        } else if (initializations.length < 16) {
            this.array = new int[16];
            arraycopy(initializations, 0, this.array, 0, initializations.length);
            this.size = initializations.length;
        } else {
            this.array = new int[(int) (initializations.length * 1.5)];
            arraycopy(initializations, 0, this.array, 0, initializations.length);
            this.size = initializations.length;
        }
    }

    /**
     * 添加一个新的元素。
     *
     * @param i
     *         被添加的元素。
     */
    public void add(
            int i
    ) {
        assert this.size <= this.array.length : "Illegal size: " + this.size + ", should not greater than " + this.array.length;

        if (this.size >= this.array.length) {
            // 扩容。
            var newarray = new int[this.array.length * 2];
            arraycopy(this.array, 0, newarray, 0, this.size);
            this.array = newarray;
        }

        this.array[this.size++] = i;

        assert this.size <= this.array.length : "Illegal size: " + this.size + ", should not greater than " + this.array.length;
    }

    /**
     * 在此列表的末尾追加另一个列表。
     *
     * @param src
     *         另一个列表。
     * @throws IllegalArgumentException
     *         如果参数{@code src}是{@code null}。
     */
    public void addAll(
            IntArrayList src
    ) {
        assert this.size <= this.array.length : "Illegal size: " + this.size + ", should not greater than " + this.array.length;

        rejectIfNull(src, "src");

        if (this == src) {
            return;
        }
        if (src.size == 0) {
            return;
        }

        final var newsize = this.size + src.size;
        if (newsize >= this.array.length) {
            // 扩容。
            var newarray = new int[(int) (newsize * 1.5)];
            arraycopy(this.array, 0, newarray, 0, this.size);
            this.array = newarray;
        }

        arraycopy(src.array, 0, this.array, this.size, src.size);
        this.size += src.size;

        assert this.size <= this.array.length : "Illegal size: " + this.size + ", should not greater than " + this.array.length;
    }

    /**
     * 获取指定序号的值。
     *
     * @param index
     *         从{@code 0}开始的序号。
     * @return 指定序号的值。
     * @throws ArrayIndexOutOfBoundsException
     *         如果序号小于 {@code 0} 或者大于等于 {@code size}。
     * @see #size()
     */
    public int get(
            int index
    ) {
        rejectIfOutOfRange(index, 0, this.size, "index");

        return this.array[index];
    }

    /**
     * 清除所有数据，{@link #size()} 被重置为 {@code 0}。
     */
    public void clear() {
        this.array = new int[16];
        this.size = 0;
    }

    public int indexOf(
            int v
    ) {
        for (var i = 0; i < this.size; ++i) {
            if (this.array[i] == v) {
                return i;
            }
        }

        return -1;
    }

    /**
     * 将列表转化为整数数组。
     *
     * @return 包含所有元素的整数数组。
     */
    public int[] toArray() {
        if (this.size == 0) {
            return new int[0];
        } else {
            return copyOf(this.array, this.size);
        }
    }

    /**
     * 将列表转化为流。
     *
     * @return 包含了所有元素的流。
     */
    public IntStream stream() {
        return intStream(spliterator(this.array, 0, this.size), false);
    }

    /**
     * 判断此列表中是否包含指定的数字。
     *
     * @param v
     *         指定的数字。
     * @return 列表中是否包含指定的数字。
     */
    public boolean contains(
            int v
    ) {
        for (var i = 0; i < this.size; ++i) {
            if (this.array[i] == v) {
                return true;
            }
        }

        return false;
    }

    /**
     * 获取列表的长度。
     *
     * @return 列表的长度。
     */
    public int size() {
        return this.size;
    }

    /**
     * 判断列表是否为空。
     *
     * @return 列表是否为空。
     */
    public boolean isEmpty() {
        return this.size == 0;
    }
}
